// 此处放的是一些工具类
#pragma once

#include <algorithm>
#include <string>
#include <vector>
#include <fstream>
#include <iostream>
#include <mutex>
#include <jsoncpp/json/json.h>
// #include <json/json.h>
#include <boost/algorithm/string.hpp>
#include "errno_num.h"
#include "cppjieba/Jieba.hpp"
#include "log.hpp"
#include <mysql++/mysql++.h>

extern Log lg;


const std::string files_folder = "./data/input";
const std::string output = "./data/output/raw.txt";
const std::string boost_version = "1_84_0";

const std::string host_name = "118.178.95.219";
const std::string user_name = "lin_zizhuo";
const std::string passwd = "@Lzz1935738";
const std::string database = "boost_searcher_index";
const std::string table_forward_index = "forward_index";
const std::string table_inverted_index = "inverted_index";
const unsigned int port = 3306;

namespace ns_util
{
    class file_util
    {
    public:
        static bool read_file(const std::string& file, std::string* content)
        {
            std::ifstream fin(file);
            if (!fin.is_open())
            {
                // std::cerr << "open file \"" << file << "\"error" << std::endl;
                lg(Error, "open file \"%s\" error", file.c_str());
                return Open_file_err;
            }
            std::string line, text;
            while (std::getline(fin, line))
                text += line;
            // 这里有拷贝问题
            *content = std::move(text);
            fin.close();
            return true;
        }
    };
    class string_util
    {
    public:
        static size_t cut_string(const std::string& content, std::vector<std::string>* result, const std::string& sep)
        {
            boost::algorithm::split(*result, content, boost::is_any_of(sep), boost::token_compress_on);
            return result->size();
        }
        static size_t search_ignore_case(const std::string& str, const std::string& need)
        {
            auto it = std::search(str.begin(), str.end(), need.begin(), need.end(), [](const char c1, const char c2)
                { return std::toupper(c1) == std::toupper(c2); });
            return it == str.end() ? std::string::npos : std::distance(str.begin(), it);
        }
    };

    const char* const DICT_PATH = "./dict/jieba.dict.utf8";
    const char* const HMM_PATH = "./dict/hmm_model.utf8";
    const char* const USER_DICT_PATH = "./dict/user.dict.utf8";
    const char* const IDF_PATH = "./dict/idf.utf8";
    const char* const STOP_WORD_PATH = "./dict/stop_words.utf8";
    // 通过测试，cut_for_search仍然会保留停用词，所以需要把停用词删去
    // 同时可以将jieba_util设置成单例
    class jieba_util
    {
    private:
        // 为防止多次创建jieba对象，使用静态成员
        cppjieba::Jieba _jieba;
        std::unordered_set<std::string> _stop_word_map;
        static std::mutex mtx;
    private:
        jieba_util()
            :_jieba(DICT_PATH, HMM_PATH, USER_DICT_PATH, IDF_PATH, STOP_WORD_PATH) {}
        ~jieba_util() = default;
        jieba_util(const jieba_util&) = delete;
        jieba_util& operator=(const jieba_util&) = delete;
        static jieba_util* ins;

    private:
        void init_stop_word()
        {
            std::ifstream in(STOP_WORD_PATH);
            if (!in.is_open())
            {
                lg(Error, "stop word file can't open success!");
                return;
            }
            std::string word;
            // while(in >> word)
            while (std::getline(in, word))
                _stop_word_map.insert(word);
        }

        void delete_stop_word(std::vector<std::string>* pwords)
        {
            // const auto &stop_word = _stop_word_map;
            pwords->erase(std::remove_if(pwords->begin(), pwords->end(), [&](const std::string& word) {
                return _stop_word_map.count(word) != 0;
                }), pwords->end());
        }

    public:
        static jieba_util* get_instance()
        {
            if (nullptr == ins)
            {
                mtx.lock();
                if (nullptr == ins)
                    ins = new jieba_util();
                mtx.unlock();
            }
            // 在获取单例的时候顺便初始化一下暂停词
            ins->init_stop_word();
            return ins;
        }

        void cut_for_search(const std::string& text, std::vector<std::string>* pwords)
        {
            _jieba.CutForSearch(text, *pwords);
            delete_stop_word(pwords);
        }
    };
    jieba_util* jieba_util::ins = nullptr;
    std::mutex jieba_util::mtx;

    class json_util
    {
    private:
        static Json::StreamWriterBuilder w_builder;
        static Json::CharReaderBuilder r_builder;

    public:
        static void change_out_sep(const std::string& sep)
        {
            w_builder["indentation"] = sep;
        }
        static std::string serialize(const Json::Value& root)
        {
            return Json::writeString(w_builder, root);
        }
        static bool parse_json(const std::string& str, Json::Value* val)
        {
            Json::Value root;
            std::unique_ptr<Json::CharReader> reader(r_builder.newCharReader());
            std::string errors;
            bool parse_sucess = reader->parse(str.c_str(), str.c_str() + str.size(), &root, &errors);
            if (!parse_sucess)
            {
                lg(Error, "failed to parse JSON: %s", errors.c_str());
                return false;
            }
            *val = std::move(root);
            return true;
        }
    };
    Json::StreamWriterBuilder json_util::w_builder;
    Json::CharReaderBuilder json_util::r_builder;

}